﻿namespace DotRas
{
    using DotRas.Design;
    using pptpclient.Properties;
    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Drawing;
    using System.Drawing.Design;
    using System.Net;
    using System.Security.Permissions;
    using System.Threading;
    using System.Windows.Forms;

    [ToolboxBitmap(typeof(RasDialer), "DotRas.RasDialer.bmp"), ToolboxItem(true), PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")]
    public sealed class RasDialer : RasComponent
    {
        private AsyncOperation _asyncOp;
        private string _callbackNumber;
        private bool _cancelled;
        private SendOrPostCallback _dialCompletedCallback;
        private bool _dialing;
        private RasEapInfo _eapData;
        private RasEapOptions _eapOptions;
        private string _entryName;
        private RasHandle _handle;
        private RasDialExtensionsOptions _options;
        private IWin32Window _owner;
        private string _phoneBookPath;
        private string _phoneNumber;
        private DotRas.NativeMethods.RasDialFunc2 _rasDialCallback;
        private int _subEntryId;
        private readonly object _syncRoot;
        private int _timeout;
        private TimerCallback _timeoutCallback;
        private System.Threading.Timer _timer;

        [DotRas.SRCategory("CatBehavior"), DotRas.SRDescription("RDDialCompletedDesc")]
        public event EventHandler<DialCompletedEventArgs> DialCompleted;

        [DotRas.SRCategory("CatBehavior"), DotRas.SRDescription("RDStateChangedDesc")]
        public event EventHandler<StateChangedEventArgs> StateChanged;

        public RasDialer()
        {
            this._syncRoot = new object();
            this.InitializeComponent();
        }

        public RasDialer(IContainer container) : base(container)
        {
            this._syncRoot = new object();
            this.InitializeComponent();
        }

        [SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)]
        private void Abort()
        {
            lock (this._syncRoot)
            {
                if ((this._handle != null) && !this._handle.IsInvalid)
                {
                    DotRas.RasHelper.HangUp(this._handle);
                }
            }
        }

        public RasHandle Dial()
        {
            return this.Dial(null);
        }

        public RasHandle Dial(NetworkCredential credentials)
        {
            return this.InternalDial(credentials, false);
        }

        public RasHandle DialAsync()
        {
            return this.DialAsync(null);
        }

        public RasHandle DialAsync(NetworkCredential credentials)
        {
            return this.InternalDial(credentials, true);
        }

        public void DialAsyncCancel()
        {
            lock (this._syncRoot)
            {
                if (this._dialing && !this._cancelled)
                {
                    this._cancelled = true;
                    this.Abort();
                    this.PostCompleted(null, true, false, false);
                }
            }
        }

        private void DialCompletedCallback(object state)
        {
            DialCompletedEventArgs e = (DialCompletedEventArgs) state;
            this.OnDialCompleted(e);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this._dialing)
                {
                    this.Abort();
                    this._handle = null;
                    this._asyncOp = null;
                }
                if (this._timer != null)
                {
                    this._timer.Dispose();
                    this._timer = null;
                }
                this._dialCompletedCallback = null;
                this._rasDialCallback = null;
                this._timeoutCallback = null;
            }
            base.Dispose(disposing);
        }

        public ReadOnlyCollection<RasConnection> GetActiveConnections()
        {
            return DotRas.RasHelper.GetActiveConnections();
        }

        protected override void InitializeComponent()
        {
            this._timeout = -1;
            this._dialCompletedCallback = new SendOrPostCallback(this.DialCompletedCallback);
            this._timeoutCallback = new TimerCallback(this.TimeoutCallback);
            this._rasDialCallback = new DotRas.NativeMethods.RasDialFunc2(this.RasDialCallback);
            base.InitializeComponent();
        }

        private RasHandle InternalDial(NetworkCredential credentials, bool asynchronous)
        {
            if (string.IsNullOrEmpty(this.EntryName) && string.IsNullOrEmpty(this.PhoneNumber))
            {
                DotRas.ThrowHelper.ThrowInvalidOperationException(Resources.Exception_PhoneNumberOrEntryNameRequired);
            }
            if (!string.IsNullOrEmpty(this.EntryName) && string.IsNullOrEmpty(this.PhoneBookPath))
            {
                DotRas.ThrowHelper.ThrowArgumentException("PhoneBookPath", Resources.Argument_StringCannotBeNullOrEmpty);
            }
            lock (this._syncRoot)
            {
                if (!this._dialing)
                {
                    this._dialing = true;
                    this._cancelled = false;
                    try
                    {
                        DotRas.NativeMethods.RASDIALPARAMS parameters = new DotRas.NativeMethods.RASDIALPARAMS {
                            callbackId = IntPtr.Zero,
                            subEntryId = this.SubEntryId
                        };
                        if (!string.IsNullOrEmpty(this.CallbackNumber))
                        {
                            parameters.callbackNumber = this.CallbackNumber;
                        }
                        if (!string.IsNullOrEmpty(this.EntryName))
                        {
                            parameters.entryName = this.EntryName;
                        }
                        if (!string.IsNullOrEmpty(this.PhoneNumber))
                        {
                            parameters.phoneNumber = this.PhoneNumber;
                        }
                        if (credentials == null)
                        {
                            NetworkCredential credential = DotRas.RasHelper.GetCredentials(this.PhoneBookPath, this.EntryName, DotRas.NativeMethods.RASCM.Domain | DotRas.NativeMethods.RASCM.Password | DotRas.NativeMethods.RASCM.UserName);
                            if (credential != null)
                            {
                                parameters.userName = credential.UserName;
                                parameters.password = credential.Password;
                                parameters.domain = credential.Domain;
                                credential = null;
                            }
                        }
                        else
                        {
                            parameters.userName = credentials.UserName;
                            parameters.password = credentials.Password;
                            parameters.domain = credentials.Domain;
                        }
                        DotRas.NativeMethods.RASDIALEXTENSIONS extensions = new DotRas.NativeMethods.RASDIALEXTENSIONS {
                            options = this.Options
                        };
                        if (this.EapData != null)
                        {
                            DotRas.NativeMethods.RASEAPINFO raseapinfo = new DotRas.NativeMethods.RASEAPINFO {
                                eapData = this.EapData.EapData,
                                sizeOfEapData = this.EapData.SizeOfEapData
                            };
                            extensions.eapInfo = raseapinfo;
                        }
                        if (this.Owner != null)
                        {
                            extensions.handle = this.Owner.Handle;
                        }
                        DotRas.NativeMethods.RasDialFunc2 callback = null;
                        if (asynchronous)
                        {
                            callback = this._rasDialCallback;
                            this._asyncOp = AsyncOperationManager.CreateOperation(null);
                            if (this._timer != null)
                            {
                                this._timer.Dispose();
                                this._timer = null;
                            }
                            if (this._timeout != -1)
                            {
                                this._timer = new System.Threading.Timer(this._timeoutCallback, null, this.Timeout, -1);
                            }
                        }
                        this._handle = DotRas.RasHelper.Dial(this.PhoneBookPath, parameters, extensions, callback, this.EapOptions);
                        if (!asynchronous)
                        {
                            this._dialing = false;
                        }
                    }
                    catch (Exception)
                    {
                        this._dialing = false;
                        throw;
                    }
                }
            }
            return this._handle;
        }

        private void OnDialCompleted(DialCompletedEventArgs e)
        {
            base.RaiseEvent<DialCompletedEventArgs>(this.DialCompleted, e);
        }

        private void OnStateChanged(StateChangedEventArgs e)
        {
            base.RaiseEvent<StateChangedEventArgs>(this.StateChanged, e);
        }

        private void PostCompleted(Exception error, bool cancelled, bool timedOut, bool connected)
        {
            lock (this._syncRoot)
            {
                this._dialing = false;
                if (this._asyncOp != null)
                {
                    this._asyncOp.PostOperationCompleted(this._dialCompletedCallback, new DialCompletedEventArgs(error, cancelled, timedOut, connected, this._handle));
                    this._asyncOp = null;
                    this._handle = null;
                    this._timer = null;
                }
            }
        }

        private bool RasDialCallback(int callbackId, int subEntryId, IntPtr dangerousHandle, int message, RasConnectionState state, int errorCode, int extendedErrorCode)
        {
            bool flag = true;
            lock (this._syncRoot)
            {
                if (!this._dialing || this._cancelled)
                {
                    return false;
                }
                string errorMessage = null;
                if (errorCode != 0)
                {
                    errorMessage = DotRas.RasHelper.GetRasErrorString(errorCode);
                }
                StateChangedEventArgs e = new StateChangedEventArgs(callbackId, subEntryId, new RasHandle(dangerousHandle), state, errorCode, errorMessage, extendedErrorCode);
                this.OnStateChanged(e);
                if (state == RasConnectionState.Connected)
                {
                    this.PostCompleted(null, false, false, true);
                    return flag;
                }
                if (errorCode != 0)
                {
                    this.Abort();
                    this.PostCompleted(new RasDialException(errorCode, extendedErrorCode), false, false, false);
                    flag = false;
                }
            }
            return flag;
        }

        private void TimeoutCallback(object state)
        {
            lock (this._syncRoot)
            {
                if (this._dialing && !this._cancelled)
                {
                    this.Abort();
                    this.PostCompleted(new TimeoutException(Resources.Exception_OperationTimedOut), false, true, false);
                }
            }
        }

        [DefaultValue((string) null), DotRas.SRCategory("CatData"), DotRas.SRDescription("RDCallbackNumberDesc")]
        public string CallbackNumber
        {
            get
            {
                return this._callbackNumber;
            }
            set
            {
                this._callbackNumber = value;
            }
        }

        [Browsable(false)]
        public RasEapInfo EapData
        {
            get
            {
                return this._eapData;
            }
            set
            {
                this._eapData = value;
            }
        }

        [DotRas.SRCategory("CatBehavior"), DefaultValue(typeof(RasEapOptions), "None"), DotRas.SRDescription("RDEapOptionsDesc")]
        public RasEapOptions EapOptions
        {
            get
            {
                return this._eapOptions;
            }
            set
            {
                this._eapOptions = value;
            }
        }

        [DotRas.SRCategory("CatData"), DotRas.SRDescription("REDEntryNameDesc"), DefaultValue((string) null)]
        public string EntryName
        {
            get
            {
                return this._entryName;
            }
            set
            {
                this._entryName = value;
            }
        }

        [DotRas.SRCategory("CatBehavior"), DotRas.SRDescription("RDEntryOptionsDesc"), DefaultValue(typeof(RasDialExtensionsOptions), "None")]
        public RasDialExtensionsOptions Options
        {
            get
            {
                return this._options;
            }
            set
            {
                this._options = value;
            }
        }

        [DotRas.SRCategory("CatBehavior"), DotRas.SRDescription("RDOwnerDesc"), DefaultValue((string) null)]
        public IWin32Window Owner
        {
            get
            {
                return this._owner;
            }
            set
            {
                this._owner = value;
            }
        }

        [Editor(typeof(PhoneBookFileNameEditor), typeof(UITypeEditor)), DefaultValue((string) null), DotRas.SRCategory("CatData"), DotRas.SRDescription("RDPhoneBookPathDesc")]
        public string PhoneBookPath
        {
            get
            {
                return this._phoneBookPath;
            }
            set
            {
                this._phoneBookPath = value;
            }
        }

        [DefaultValue((string) null), DotRas.SRCategory("CatData"), DotRas.SRDescription("REDPhoneNumberDesc")]
        public string PhoneNumber
        {
            get
            {
                return this._phoneNumber;
            }
            set
            {
                this._phoneNumber = value;
            }
        }

        [DotRas.SRCategory("CatData"), DefaultValue(0), DotRas.SRDescription("RDSubEntryIdDesc")]
        public int SubEntryId
        {
            get
            {
                return this._subEntryId;
            }
            set
            {
                this._subEntryId = value;
            }
        }

        [DefaultValue(-1), DotRas.SRCategory("CatBehavior"), DotRas.SRDescription("RDTimeoutDesc")]
        public int Timeout
        {
            get
            {
                return this._timeout;
            }
            set
            {
                this._timeout = value;
            }
        }
    }
}

